
#include <alsa/asoundlib.h>
#include <alsa/control.h>
#include <alsa/pcm.h>
#include <glib-2.0/glib.h>

 struct alsa_device 
{
    guint card;
    guint dev;
    gchar info[80];
    gboolean is_available;
};
typedef struct alsa_device alsa_device;    


alsa_device *create_alsa_array(size_t n) { 
    alsa_device *new = malloc(n * sizeof *new); 
    return new; 
};

void alsa_get_hardware_string(char *resultstr,struct alsa_device *alsa_dev, guint8 position) {
    sprintf(resultstr,"hw:%d,%d",alsa_dev[position].card,alsa_dev[position].dev);
}

char *alsa_get_hardware_string_allocated(struct alsa_device *alsa_dev, guint8 position) {
    char *alsa_device_hardware_string = malloc(10);
    sprintf(alsa_device_hardware_string,"hw:%d,%d",alsa_dev[position].card,alsa_dev[position].dev);
    return alsa_device_hardware_string;
}

char *alsa_get_hardware_desc(struct alsa_device *alsa_dev, guint8 position) {
    return alsa_dev[position].info;
}

char *alsa_get_hardware_desc_alocated(struct alsa_device *alsa_dev, guint8 position) {
    char *alsa_desc = alsa_dev[position].info;
    gsize alsa_desc_ln = strlen(alsa_desc)+1;
    char *alsa_desc_allocated = malloc(alsa_desc_ln);
    memcpy(alsa_desc_allocated, alsa_desc, alsa_desc_ln);
    return alsa_desc_allocated;
}

gboolean alsa_get_hardware_is_available(struct alsa_device *alsa_dev,guint8 position) {
    return alsa_dev[position].is_available;
}

void alsa_get_device_list(snd_pcm_stream_t stream,alsa_device *devices, gsize *devices_cnt)
{
    
    int devices_count=0;
    char *hw_device;
    
	snd_ctl_t *handle;
        snd_pcm_t *pcm_handle;
	int card, err, dev, idx,rc;
	snd_ctl_card_info_t *info;
	snd_pcm_info_t *pcminfo;
	snd_ctl_card_info_alloca(&info);
	snd_pcm_info_alloca(&pcminfo);
        hw_device=malloc(10);

	card = -1;
	if (snd_card_next(&card) < 0 || card < 0) {
		g_error("no soundcards found...");
		return;
	}
	     //  snd_pcm_stream_name(stream);
	while (card >= 0) {
		char name[32];
              sprintf(name, "hw:%d", card);
		if ((err = snd_ctl_open(&handle, name, 0)) < 0) {
			g_error("control open (%i): %s", card, snd_strerror(err));
			goto next_card;
		}
		if ((err = snd_ctl_card_info(handle, info)) < 0) {
			g_error("control hardware info (%i): %s", card, snd_strerror(err));
			snd_ctl_close(handle);
			goto next_card;
		}
		dev = -1;
		while (1) {
			unsigned int count;
			if (snd_ctl_pcm_next_device(handle, &dev)<0)
				g_error("snd_ctl_pcm_next_device");
			if (dev < 0)
				break;
			snd_pcm_info_set_device(pcminfo, dev);
			snd_pcm_info_set_subdevice(pcminfo, 0);
			snd_pcm_info_set_stream(pcminfo, stream);
			if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
				if (err != -ENOENT)
					g_error("control digital audio info (%i): %s", card, snd_strerror(err));
				continue;
			}
                        
                        sprintf(hw_device,"%s,%d",name,dev);
                        rc = snd_pcm_open(&pcm_handle,hw_device,stream,0);
                        if (rc < 0) {
                            g_debug("unable to open pcm device: %s",snd_strerror(rc));
                            devices[devices_count].is_available=FALSE;                        
                        } else {
                            snd_pcm_close(pcm_handle);
                            devices[devices_count].is_available=TRUE;                        
                        }
                        devices[devices_count].card=card;
                        devices[devices_count].dev=dev;
                        
                        sprintf(devices[devices_count].info,"%s",snd_ctl_card_info_get_name(info));
                        devices_count++;			
                        g_debug("card %i: %s [%s], device %i: %s [%s]\n",
				card, snd_ctl_card_info_get_id(info), snd_ctl_card_info_get_name(info),
				dev,
				snd_pcm_info_get_id(pcminfo),
				snd_pcm_info_get_name(pcminfo));
                 
			count = snd_pcm_info_get_subdevices_count(pcminfo);
			g_debug( "  Subdevices: %i/%i\n",
				snd_pcm_info_get_subdevices_avail(pcminfo), count);
			for (idx = 0; idx < (int)count; idx++) {
				snd_pcm_info_set_subdevice(pcminfo, idx);
				if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
					g_error("control digital audio playback info (%i): %s", card, snd_strerror(err));
				} else {
					g_debug("  Subdevice #%i: %s\n",
						idx, snd_pcm_info_get_subdevice_name(pcminfo));
				}
			}
		}
		snd_ctl_close(handle);
	next_card:
		if (snd_card_next(&card) < 0) {
			g_error("snd_card_next");
			break;
		}
	}
        *devices_cnt=devices_count;
        free(hw_device);
}


void alsa_get_capture_device_list(alsa_device *devices, gsize *devices_cnt) {
    alsa_get_device_list(SND_PCM_STREAM_CAPTURE,devices,devices_cnt);
}

void alsa_get_playback_device_list(alsa_device *devices, gsize *devices_cnt) {
    alsa_get_device_list(SND_PCM_STREAM_PLAYBACK,devices,devices_cnt);
}


/*void pcm_list(void)
{
	void **hints, **n;
	char *name, *descr, *descr1, *io;
	const char *filter;

	if (snd_device_name_hint(-1, "pcm", &hints) < 0)
		return;
	n = hints;
	filter = stream == SND_PCM_STREAM_CAPTURE ? "Input" : "Output";
	while (*n != NULL) {
		name = snd_device_name_get_hint(*n, "NAME");
		descr = snd_device_name_get_hint(*n, "DESC");
		io = snd_device_name_get_hint(*n, "IOID");
		if (io != NULL && strcmp(io, filter) != 0)
			goto __end;
		printf("%s\n", name);
		if ((descr1 = descr) != NULL) {
			printf("    ");
			while (*descr1) {
				if (*descr1 == '\n')
					printf("\n    ");
				else
					putchar(*descr1);
				descr1++;
			}
			putchar('\n');
		}
	      __end:
	      	if (name != NULL)
	      		free(name);
		if (descr != NULL)
			free(descr);
		if (io != NULL)
			free(io);
		n++;
	}
	snd_device_name_free_hint(hints);
}*/


    
 